package Hashing;

import java.util.*;

public class Fingerprint {
	 public static final double scaling = FFT.WINDOW_SIZE / 44100.0;

	    public static final int N = 3;

	    public ArrayList<int[]> constel_data = new ArrayList<>(); 
	    private int id;

	    public Fingerprint(int id) {
	        this.id = id;
	    }

	    public Fingerprint() {
	        this.id = -1;
	    }

	    public void append(double[] freqDomain) 
	    {
	    	double[] freqDomain2 = new double[496];
	    	System.arraycopy(freqDomain, 5, freqDomain2, 0, 496);
	    	
	        double[] freqPeaksTemp = new double[N];
	        int[] freqPeaks = new int[N];

	        find(freqDomain2,freqPeaksTemp,0,495,N);

	        for(int i =0;i<N;i++)
	        {
	        	for(int j=5;j<501;j++)
	        	{
	        		if(freqDomain[j] == freqPeaksTemp[i])
	        		{
	        			freqPeaks[i] = j;
	        		}
	        	}
	        }
	        constel_data.add(freqPeaks);
	    }
	    
	    public void find(double[] freqDomain, double[] res,int left,int right,int num)
	    {
	    	if(right-left+1==num)
	    	{
	    		System.arraycopy(freqDomain, 0, res, 0, N);
				return;
	    	}
	    	int i = left;
			int j = right;
			double x = freqDomain[left];
			double temp;
			while(i<j)
			{
				while(freqDomain[j] < x && i<j)
				{
					j --;
				}
				while(freqDomain[i] >= x && i<j)
				{
					i ++ ;
				}
				temp = freqDomain[i];
	    		freqDomain[i] = freqDomain[j];
	    		freqDomain[j] = temp;
			}
			
			temp = freqDomain[left];
			freqDomain[left] = freqDomain[i];
			freqDomain[i] = temp;
			
			if((i+1-left)==num)
			{
				System.arraycopy(freqDomain, 0, res, 0, N);
				return;
			}
			if((i+1-left)>num)
			{
				find(freqDomain,res,left,j-1,num);
			}
			else
			{
				find(freqDomain,res,j+1,right,num-i-1);
			}
	    }
	    
	    public void find2(ArrayList<double[]> freqDomain, ArrayList<double[]> res,int left,int right,int num)
	    {
	    	if(right-left+1==num)
	    	{
	    		for(int i=0;i<N;i++)
		    	{
		    		res.add(freqDomain.get(i));
		    	}
				return;
	    	}
	    	int i = left;
			int j = right;
			double x = freqDomain.get(left)[1];
			double temp;
			while(i<j)
			{
				while(freqDomain.get(j)[1] < x && i<j)
				{
					j --;
				}
				while(freqDomain.get(i)[1] >= x && i<j)
				{
					i ++ ;
				}
				temp = freqDomain.get(i)[1];
				freqDomain.get(i)[1] = freqDomain.get(j)[1];
				freqDomain.get(j)[1] = temp;
				
				temp = freqDomain.get(i)[0];
				freqDomain.get(i)[0] = freqDomain.get(j)[0];
				freqDomain.get(j)[0] = temp;
			}
			
			temp = freqDomain.get(i)[1];
			freqDomain.get(i)[1] = freqDomain.get(j)[1];
			freqDomain.get(j)[1] = temp;
			
			temp = freqDomain.get(i)[0];
			freqDomain.get(i)[0] = freqDomain.get(j)[0];
			freqDomain.get(j)[0] = temp;
			
			if((i+1-left)==num)
			{
				for(int k=0;i<N;i++)
		    	{
		    		res.add(freqDomain.get(k));
		    	}
				return;
			}
			if((i+1-left)>num)
			{
				find2(freqDomain,res,left,j-1,num);
			}
			else
			{
				find2(freqDomain,res,j+1,right,num-i-1);
			}
	    }

	    public ArrayList<ShazamHash> combineHash() {
	        if (constel_data.size() < 3)
	            throw new RuntimeException("Too few frequency peaks");
	        ArrayList<ShazamHash> hashes = new ArrayList<>();
	        for (int i = 0; i < constel_data.size() - 2; ++i) {
	            for (int k = 0; k < N; ++k) {
	                for (int j = 1; j <= 2; ++j) {
	                    for (int kk = 0; kk < N; ++kk) {
	                        ShazamHash hash = new ShazamHash();
	                        hash.f1 = (short)constel_data.get(i)[k];
	                        hash.f2 = (short)constel_data.get(i + j)[kk];
	                        hash.dt = (short) j;
	                        hash.offset = i;
	                        hash.song_id = id;
							hash.finger_id = (j<<18)|(constel_data.get(i)[k]<<9)|(constel_data.get(i + j)[kk]);
	                        hashes.add(hash);
	                    }
	                }
	            }
	        }
	        return hashes;
	    }
}
